/*
 * Script.h
 *
 *  Created on: 28 жовт. 2012
 *      Author: Houdini
 */

#ifndef SCRIPT_H_
#define SCRIPT_H_

#include <string>
#include <vector>
#include <Python.h>

#include "Exept/FuncResultError.h"
#include "Exept/ScriptNotFound.h"
#include "Exept/ArgumentError.h"

#include <iostream>
#include <typeinfo>

namespace PInter
{

class Script
{
	PyObject *pModule, *pValue, *pArgs, *pFunc;
	PyObject *pName;
	std::string name;
	std::vector<PyObject*> arguments;

	PyObject* convertToPyType(std::string value);
	PyObject* convertToPyType(double value);
	PyObject* convertToPyType(int value);

	std::string convertFromPyType(PyObject* value)
	{
		return PyUnicode_AsUTF8(pValue);
	}

	template <typename T>
	void addArgument(T value)
	{
		pValue = convertToPyType(value);
		if (!pValue) {
			Py_DECREF(pArgs);
			Py_DECREF(pModule);
			throw ArgumentError("Error creating arg list");
		}
		arguments.push_back(pValue);
	}
public:
	Script(std::string Name);
	virtual ~Script();

	template <typename T>
	T CallFunctionV()
	{
		T value;
		pValue = PyObject_CallObject(pFunc, pArgs);
		Py_DECREF(pArgs);
		if (pValue != NULL)
		{
			if (typeid(value) == typeid(std::string))
				convertFromPyType(pValue);
			else if (typeid(value) == typeid(int))
				value = PyLong_AsLong(pValue);
			else if (typeid(value) == typeid(double))
				value = PyFloat_AsDouble(pValue);

			Py_DECREF(pValue);

			return value;
		}

		Py_DECREF(pFunc);
		Py_DECREF(pModule);
		PyErr_Print();
		throw FuncResultError();
	}

	template <typename T1, typename T2>
	T1 CallFunction(const T2& value)
	{
		addArgument(value);

		pArgs = PyTuple_New(arguments.size());
		int i = 0;
		std::vector<PyObject*>::iterator iter;
		for (iter = arguments.begin(); iter != arguments.end(); ++iter)
		{
			PyTuple_SetItem(pArgs, i, *iter);
			i++;
		}

		return CallFunctionV<T1>();
	}

	template <typename T1, typename T2, typename... Args>
	T1 CallFunction(std::string Name, const T2& value, const Args&... args)
	{
		pFunc = PyObject_GetAttrString(pModule, Name.c_str());
		addArgument(value);
		return CallFunction<T1>(args...);
	}
};

} /* namespace PInter */
#endif /* SCRIPT_H_ */
